﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Web.Script.Serialization;
using System.Reflection;

namespace Utilities
{

    public static class ExtensionsObjects
    {
        /// <summary>
        /// Converts an Object into a JSON representation
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj">Object to convert</param>
        /// <returns>returns a string with the json representation or a null if the conversions had a problem</returns>
        public static string me_CreateJSON<T>(this T obj) where T : class
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            try
            {
                return serializer.Serialize(obj);
            }
            catch (Exception) { }

            return null;
        }

        /// <summary>
        /// Converts a JSON string into an object specified by us
        /// </summary>
        /// <param name="json"></param>
        /// <param name="objectType">the Type of the object we want to convert the JSON to.</param>
        /// <returns>returns an Object of the type specified or a null if the conversion fails</returns>
        public static dynamic me_CreateObjectFromJSON(this string json, string typeName)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            try
            {
                return serializer.Deserialize(json, Type.GetType(typeName));
            }
            catch (Exception)
            { }
            return null;
        }

        public static dynamic me_CreateObjectFromJSON(this string json, Type type)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            try
            {
                return serializer.Deserialize(json, type);
            }
            catch (Exception)
            { }
            return null;
        }

        public static T me_GetValue<T>(this PropertyInfo prop, T value)
        {
            Type type = prop.PropertyType;

            if (value == null && type.IsValueType && Nullable.GetUnderlyingType(type) == null)
            {
                return (T)Activator.CreateInstance(type);
            }
            return value;
        }

        /// <summary>
        /// get all the values of a certain proterty that exist in a object
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="B"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static IEnumerable<B> me_GetPropertyValues<T, B>(this T obj)
        {
            Type type = typeof(B);
            Type objType = obj.GetType();

            PropertyInfo[] properties = objType.GetProperties();

            foreach (var prop in properties)
            {
                if (prop.GetType() == type) 
                {
                    yield return (B)prop.GetValue(obj, null);
                }
            }

        }

        #region Get objects

        //public static T me_GetFirstObject<T>(this DbContext db, Func<T, bool> predicate) where T : class
        //{
        //    //throw exceptions if values are not correct
        //    if (predicate == null) throw new NullFuncException();

        //    return db.Set<T>().First(predicate);
        //}

        //public static List<T> me_GetListObject<T>(this DbContext db, Func<T, bool> predicate) where T : class
        //{
        //    //throw exceptions if values are not correct
        //    if (predicate == null) throw new NullFuncException();

        //    return db.Set<T>().Where(predicate).ToList();
        //}

        ///// <summary>
        ///// Get object form DB with the specified primeryKey 
        ///// </summary>
        ///// <typeparam name="T">Generic type of the object to be returned</typeparam>
        ///// <param name="db">DbContext object that manages the DB</param>
        ///// <param name="id">primeryKey of the object to be returned</param>
        ///// <exception cref="EmptyGuidException"></exception> 
        ///// <returns>returns an object of the type specified by the generic or null if no object was found</returns>
        //public static T me_GetObject<T>(this DbContext db, Guid id) where T : class
        //{
        //    //throw exceptions if values are not correct
        //    id.me_IsValidThrowExceptions();

        //    return db.Set<T>().Find(id);
        //}

        ///// <summary>
        ///// Get object form DB with the specified primeryKey 
        ///// </summary>
        ///// <param name="db">DbContext object that manages the DB</param>
        ///// <param name="type">type of the object</param>
        ///// <param name="id">primeryKey of the object to be returned</param>
        ///// <exception cref="EmptyGuidException"></exception> 
        ///// <exception cref="NullOrEmptyStringException"></exception>
        ///// <returns>returns an object of the type specified or null if no object was found</returns>
        //public static dynamic me_GetObject(this DbContext db, Type type, Guid id)
        //{
        //    //throw exceptions if values are not correct
        //    id.me_IsValidThrowExceptions();
        //    if (type == null) throw new NullTypeException();

        //    return db.Set(type).Find(id);
        //}

        ///// <summary>
        ///// Get object form DB with the specified primeryKey 
        ///// </summary>
        ///// <param name="db">DbContext object that manages the DB</param>
        ///// <param name="fullName">full name of the type to be obtained</param>
        ///// <param name="id">primeryKey of the object to be returned</param>
        ///// <exception cref="EmptyGuidException"></exception> 
        ///// <exception cref="NullOrEmptyStringException"></exception>
        ///// <returns>returns an object of the type specified or null if no object was found</returns>
        //public static dynamic me_GetObject(this DbContext db, string fullName, Guid id)
        //{
        //    //throw exceptions if values are not correct
        //    id.me_IsValidThrowExceptions();
        //    if (String.IsNullOrEmpty(fullName.Trim())) throw new NullOrEmptyStringException();

        //    //get type
        //    Type type = Type.GetType(fullName);

        //    //find entity
        //    return db.Set(type).Find(id);
        //}

        # endregion

        #region Object Conversion

        public static void me_CopyValuesFromObject<T, B>(T obj, B sourceObj)
            where T : class, new()
            where B : class, new()
        {
            if (obj != null && sourceObj != null)
            {
                Type sourceType = sourceObj.GetType();
                Type destinationType = obj.GetType();

                PropertyInfo[] properties = sourceType.GetProperties();


                foreach (PropertyInfo prop in properties)
                {
                    if (prop.CanRead)
                    {

                        try
                        {
                            string propertyName = prop.Name;
                            PropertyInfo toProp = destinationType.GetProperty(propertyName);

                            if (toProp != null && toProp.CanWrite)
                            {
                                //use this to adapt one Property to the other => New Version of this
                                //Type propertyType = prop.PropertyType;
                                //Type destPropertyType = prop.PropertyType;

                                object temp = prop.GetValue(sourceObj, null);

                                toProp.SetValue(obj, prop.me_GetValue(temp), null);
                            }

                        }
                        catch (Exception) { }
                    }
                }
            }
        }

        #endregion

        #region Create Objects

        /// <summary>
        /// Create an object dynamically using Generics
        /// </summary>
        /// <typeparam name="T">Generic type that is going to be used to create the new Object</typeparam>
        /// <returns>returns a new instance of the type especified</returns>
        public static T me_CreateNewObject<T>() where T : class
        {
            return Activator.CreateInstance<T>();
        }

        /// <summary>
        /// Create an object dynamically passing a typeName
        /// </summary>
        /// <param name="typeName">name of the type we want to create the object from. Needs to be the fullName which includes the namespace</param>
        /// <returns>returns an object of the type specified</returns>
        public static dynamic me_CreateNewObject(this string typeName)
        {
            if (String.IsNullOrEmpty(typeName.Trim())) throw new NullOrEmptyStringException();

            //try to create object
            return Activator.CreateInstance(Type.GetType(typeName));
        }

        /// <summary>
        /// Create an object dynamically passing a typeName
        /// </summary>
        /// <param name="type">type of the object we want to create.</param>
        /// <returns>returns an object of the type specified</returns>
        public static dynamic me_CreateNewObject(this Type type)
        {
            return Activator.CreateInstance(type);
        }

        #endregion
    }
}
